// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#pragma once
#include "StaticMeshEditorModule.h"
#include "IAssetRegistry.h"
#include "AssetRegistryModule.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/Selection.h"
#include "Rendering/SkeletalMeshModel.h"

class UStaticMesh;
class ULODRecipe;

enum class ESimplygonTestAsset
{
	SM_Donut,
	SM_Cube
};

static TMap<ESimplygonTestAsset, FString> AssetToPath =
{
	{ ESimplygonTestAsset::SM_Donut, TEXT("/Game/Content/StaticMeshes/Donut/donut_Donut.donut_Donut") },
	{ ESimplygonTestAsset::SM_Cube, TEXT("/Game/Content/StaticMeshes/Cube/Cube.Cube") }
};

static struct SimplygonTestsHelper
{
	template<typename T>
	static T* GetAsset(ESimplygonTestAsset Asset)
	{
		const FString AssetPath = AssetToPath[Asset];
		T* Mesh = SimplygonTestsHelper::GetAssetByPath<T>(FName(AssetPath));
		if (!Mesh)
		{
			return nullptr;
		}

		return Mesh;
	}

	template<typename T>
	static T* GetAssetByPath(FName InObjectPath)
	{
		FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
		IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();

		while (AssetRegistry.IsLoadingAssets());

		FAssetData AssetData = AssetRegistry.GetAssetByObjectPath(InObjectPath);
		if (AssetData.IsValid() && AssetData.IsUAsset())
		{

			UObject* Obj = AssetData.GetAsset();
			if (Obj)
				return CastChecked<T>(Obj);

		}

		return nullptr;
	}

	template <typename T>
	static bool GetAllAssetsByTypesInProject(TArray<T*>& OutStaticMeshes)
	{
		const IAssetRegistry& Registry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
		TArray<FAssetData> DataCollection;
		Registry.GetAssetsByClass(T::StaticClass()->GetFName(), DataCollection);


		for (auto Data : DataCollection)
		{
			UObject* BaseObject = Data.GetAsset();

			if (BaseObject != nullptr && BaseObject->IsA(T::StaticClass()))
			{
				T* DerivedTypePtr = CastChecked<T>(BaseObject);
				if (DerivedTypePtr != nullptr)
				{
					OutStaticMeshes.Add(DerivedTypePtr);
				}
			}
		}

		return OutStaticMeshes.Num() > 0;
	}

	static TSharedRef<IStaticMeshEditor> OpenStaticMeshEditor(UStaticMesh* InMesh)
	{
		IStaticMeshEditorModule* StaticMeshEditorModule = &FModuleManager::LoadModuleChecked<IStaticMeshEditorModule>("StaticMeshEditor");
		return StaticMeshEditorModule->CreateStaticMeshEditor(EToolkitMode::Standalone, TSharedPtr<IToolkitHost>(), InMesh);
	}

	static UWorld* GetCurrentWorld()
	{
		UWorld* CurrentWorld = nullptr;
		for (const FWorldContext& Context : GEngine->GetWorldContexts())
		{
			if (Context.WorldType == EWorldType::PIE)
			{
				CurrentWorld = Context.World();
				break;
			}
			else if (Context.WorldType == EWorldType::Editor)
			{
				CurrentWorld = Context.World();
			}
		}

		return CurrentWorld;
	}

	static int32 GetNumTrianglesInLODModel(FSkeletalMeshLODModel* LODModel)
	{
		int32 NumTriangles = 0;

		const int32 SectionCount = LODModel->Sections.Num();
		for (int32 SectionIndex = 0; SectionIndex < SectionCount; ++SectionIndex)
		{
			NumTriangles += LODModel->Sections[SectionIndex].NumTriangles;
		}

		return NumTriangles;
	}

	static bool WaitForHLODProcessor(const double Timeout);

	static ULODRecipe* CreateLODRecipe();
};
